﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using CarShop.Data.EntityFramework.Interfaces;
using CarShop.Domain.Entities;
using CarShop.Domain.Interfaces;
using PagedList;

namespace CarShop.Data.EntityFramework
{
    public abstract class Repository<TEntity, TKey> : IRepository<TEntity, TKey>
        where TEntity : EntityBase<TKey>
        where TKey : struct 
    {
        private readonly IQueryableUnitOfWork _unitOfWork;


        protected Repository(IQueryableUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        private IQueryable<TEntity> GetByPredicateWithIncludes(Func<TEntity, bool> predicate, params string[] includes)
        {
            var query = _unitOfWork.CreateSet<TEntity>().AsQueryable();

            if (includes != null)
            {
                foreach (var include in includes)
                {
                    query = query.Include(include);
                }
            }

            var collection = predicate == null ? query : query.Where(predicate).AsQueryable();

            return collection;
        }

        public TEntity Get(TKey id)
        {
            return _unitOfWork.CreateSet<TEntity>().Find(id);
        }

        public IQueryable<TEntity> GetAll(params string[] includes)
        {
            var query = _unitOfWork.CreateSet<TEntity>().AsQueryable();

            if (includes != null)
            {
                foreach (var include in includes)
                {
                    query = query.Include(include);
                }
            }

            return query;
        }

        public IQueryable<TEntity> GetByPredicate(Func<TEntity, bool> predicate, params string[] includes)
        {
            return GetByPredicateWithIncludes(predicate, includes);
        }

        public IEnumerable<TEntity> GetPaged<TProperty>(Func<TEntity, bool> predicate, int pageIndex, int pageSize, out int totalPages,
            Expression<Func<TEntity, TProperty>> orderByExpression, bool ascending = true, params string[] includes)
        {
            var fullQuery = GetByPredicateWithIncludes(predicate, includes);

            var result = ascending
                ? new PagedList<TEntity>(fullQuery.OrderBy(orderByExpression), pageIndex, pageSize)
                : new PagedList<TEntity>(fullQuery.OrderByDescending(orderByExpression), pageIndex, pageSize);

            totalPages = result.PageCount;

            return result;
        }

        public virtual void Add(TEntity entity)
        {
            //Validate(entity);
            _unitOfWork.CreateSet<TEntity>().Add(entity);
        }

        public virtual void Update(TEntity entity)
        {
            //Validate(entity);
            _unitOfWork.SetModified(entity);
        }

        public virtual void Delete(TEntity entity)
        {
            _unitOfWork.Attach(entity);
            _unitOfWork.CreateSet<TEntity>().Remove(entity);
        }

        public virtual void Delete(TKey id)
        {
            var set = _unitOfWork.CreateSet<TEntity>();
            var item = set.Find(id);
            set.Remove(item);
        }

        protected TUnitOfWork GetUnitOfWork<TUnitOfWork>() where TUnitOfWork : class, IQueryableUnitOfWork
        {
            return _unitOfWork as TUnitOfWork;
        }

        //protected void Validate(TEntity entity)
        //{
        //    if (!Validator.IsValid(entity))
        //    {
        //        throw new ValidationException("The entity is invalid for persistance.", Validator.GetErrors(entity));
        //    }
        //}

        //private IEntityValidator Validator
        //{
        //    get { return _validator ?? (_validator = EntityValidatorFactory.CreateValidator(ValidationTag.Data)); }
        //}
    }
}
